home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 301-325 / disk_325 / fam / fam.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  34KB  |  1,382 lines

  1. /*
  2.  * FAM.c -- Rexx File Access Manager and Directory Buffer.
  3.  * Copyright 1990 Darren New.  All Rights Reserved.
  4.  * Originally for DIBBS -- Darren's Innovative Bulletin Board Server
  5.  */
  6.  
  7. /******************************************************************
  8.  
  9. This program allows multiple ARexx programs to access a buffered version of
  10. a directory in a consistent and serialized manner. This program and all
  11. derivative works are copyright 1990 Darren New. All Rights Reserved.
  12.  
  13. Permission to use, copy, modify, and distribute this software and its
  14. documentation for any purpose and without fee is hereby granted, provided
  15. that the above copyright notice appear in all copies and that both that
  16. copyright notice and this permission notice appear in supporting
  17. documentation, and that the names of the copyright holder or author not be
  18. used in advertising or publicity pertaining to disstribution of the
  19. software without specific, written prior permission.
  20.  
  21. BOTH THE AUTHOR AND THE COPYRIGHT HOLDER DISCLAIM ALL WARRANTIES WITH
  22. REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
  23. MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDER OR THE
  24. AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  25. DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  26. AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  27. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  28.  
  29.         VERSION 1.1    Feb 1990
  30.  
  31. ***********************************************************/
  32.  
  33. #define PROGNAME    "FAM"
  34. #define PROGVERS    "1.1"
  35. #define ERRLEVEL    10L     /* error level to return */
  36.  
  37. #include "stdio.h"
  38. #include "stdlib.h"
  39. #include "string.h"
  40. #include "ctype.h"
  41. #include "minrexx.h"
  42.  
  43. #ifdef LMS
  44. #include "low-mem.h"    /* ASDG's low memory server */
  45. #endif
  46.  
  47. #ifdef CBACK
  48.     #define assert(exp)
  49. #else
  50.     #include "assert.h"
  51. #endif
  52.  
  53. #include "exec/memory.h"
  54. #include "exec/lists.h"
  55. #include "libraries/dos.h"
  56. #include "proto/exec.h"
  57. #include "proto/dos.h"
  58.  
  59.  
  60. /* Because remove and insert are used very heavily during rescan, these
  61.  * have been turned into macros to let the optimizer bash at them.
  62.  * The InsertQ only works when the node to be inserted already points
  63.  * to the correct nodes to insert between.
  64.  */
  65.  
  66. #define Remove(node) ((node)->ln_Pred->ln_Succ=(node)->ln_Succ,(node)->ln_Succ->ln_Pred=(node)->ln_Pred)
  67. #define InsertQ(node) ((node)->ln_Pred->ln_Succ=(node),(node)->ln_Succ->ln_Pred=(node))
  68.  
  69. /*
  70.  *   Here is our command association list.  Note that in this case,
  71.  *   we are setting the userdata field to be a function to call.
  72.  *   Dispatch will still take place through disp(), so common head
  73.  *   and tail stuff can go there.
  74.  *
  75.  *   Commands are all lower case, so we match either upper or lower.
  76.  *   (This is a requirement of minrexx.)
  77.  */
  78.  
  79. void FAMversion       (struct RexxMsg *msg, char *p);
  80. void FAMopen          (struct RexxMsg *msg, char *p);
  81. void FAMclose          (struct RexxMsg *msg, char *p);
  82. void FAMclear          (struct RexxMsg *msg, char *p);
  83. void FAMexpunge       (struct RexxMsg *msg, char *p);
  84. void FAMgetioerr      (struct RexxMsg *msg, char *p);
  85. void FAMgetdirname      (struct RexxMsg *msg, char *p);
  86. void FAMgetbinextens      (struct RexxMsg *msg, char *p);
  87. void FAMgetbufendline      (struct RexxMsg *msg, char *p);
  88. void FAMgetnamecount      (struct RexxMsg *msg, char *p);
  89. void FAMgetnames      (struct RexxMsg *msg, char *p);
  90. void FAMgetinfo       (struct RexxMsg *msg, char *p);
  91. void FAMrescan1       (struct RexxMsg *msg, char *p);
  92. void FAMrescan          (struct RexxMsg *msg, char *p);
  93. void FAMgrep          (struct RexxMsg *msg, char *p);
  94. void FAMnewfile       (struct RexxMsg *msg, char *p);
  95. void FAMlockfile      (struct RexxMsg *msg, char *p);
  96.  
  97. struct rexxCommandList rcl[] = {
  98.    { "version", (APTR)&FAMversion },
  99.    { "open", (APTR)&FAMopen },
  100.    { "close", (APTR)&FAMclose },
  101.    { "clear", (APTR)&FAMclear },
  102.    { "expunge", (APTR)&FAMexpunge },
  103.    { "getioerr", (APTR)&FAMgetioerr },
  104.    { "getdirname", (APTR)&FAMgetdirname },
  105.    { "getbinextens", (APTR)&FAMgetbinextens },
  106.    { "getbufendline", (APTR)&FAMgetbufendline },
  107.    { "getnamecount", (APTR)&FAMgetnamecount },
  108.    { "getnames", (APTR)&FAMgetnames },
  109.    { "getinfo", (APTR)&FAMgetinfo },
  110.    { "rescan1", (APTR)&FAMrescan1 },
  111.    { "rescan", (APTR)&FAMrescan },
  112.    { "grep", (APTR)&FAMgrep },
  113.    { "newfile", (APTR)&FAMnewfile },
  114.    { "lockfile", (APTR)&FAMlockfile },
  115.    { NULL, NULL } } ;
  116.  
  117.  
  118. /********************************************************************
  119.  * GLOBAL STATUS VARIABLES
  120.  ********************************************************************/
  121.  
  122. /*
  123.  * Here are the globals used by Lattice's CBack.o
  124.  *
  125.  */
  126.  
  127. #ifdef CBACK
  128. LONG _stack = 4000;            /* stack space */
  129. char *_procname =  PROGNAME "-task";/* process name */
  130. LONG _priority = 0;            /* default priority */
  131. LONG _BackGroundIO = 1;         /* I want to output some messages */
  132. extern LONG _Backstdout;        /* FileHandle to output to */
  133. #endif
  134.  
  135.  
  136. /*
  137.  * Here are the globals for keeping track of our command-line arguments
  138.  * and our current status.
  139.  *
  140.  */
  141.  
  142. char VersionString[] = PROGNAME " V" PROGVERS " COMPILED " __DATE__ " " __TIME__;
  143.  
  144. char * PortName;        /* name of ARexx port to open */
  145. char * DirPath;         /* full path to directory */
  146. char * GrepFuncName;        /* full path to GREP to LoadSeg() */
  147. char * BinExtens;        /* file extensions of "binary" files */
  148. char * BufEndLine;        /* line to end buffering */
  149.  
  150. int OpenCount;            /* number of outstanding OPENs */
  151. int ExpungeWanted;        /* have received the EXPUNGE command */
  152. int ClearWanted;        /* have received the CLEAR command */
  153. BPTR GrepEntry;         /* GREP function entry point */
  154.  
  155. #ifdef LMS
  156. struct Library * LowMemBase;/* ASDG Low Memory Base address */
  157. struct LowMemMsg LMSSpace;  /* ASDG low memory notifier */
  158. int LowMemIsOpen;        /* true if we are handling LowMem stuff */
  159. #endif
  160.  
  161. /*
  162.  * Here are the globals for holding the directory information
  163.  *
  164.  */
  165.  
  166. #include "FAM.h"
  167.  
  168. struct List ScanList;        /* the actual ScanList */
  169. struct List TempScanList;   /* an alternate copy (for sorting) */
  170.  
  171.     /* returns true if list is empty */
  172. #define IsEmpty(head) ((long) ((head).lh_TailPred) == (long) (&(head)))
  173.  
  174.     /* returns pointer to first node on list */
  175. #define FirstNode(head) ((struct ScanListNode *)((head)->lh_Head->ln_Succ))
  176.  
  177.     /* returns pointer to next node on list */
  178. #define NextNode(n) ((struct ScanListNode *)((n)->node.ln_Succ))
  179.  
  180.     /* returns true if node is valid ScanListNode */
  181. #define NotLastNode(n) ((n)&&(n)->node.ln_Succ)
  182.  
  183.     /* returns true if a < b */
  184. #define StrLT(a, b) (strcmp((a), (b)) < 0)
  185.  
  186. /*
  187.  * These globals are passed around during parsing.
  188.  *
  189.  */
  190.  
  191. long CMDargs[9];    /* what numeric args did we see to this function? */
  192. int CMDparsed;        /* 0 if argument parsing successful, !0 for errnum */
  193. int CMDuserreplied; /* has the current message been replied to yet? */
  194.  
  195. /*
  196.  * mallocFAM(), freeFAM() and strdupFAM() have the same interface as
  197.  * malloc(), free(), and strdup() except that they actually return the
  198.  * memory back to the system once it is freed.
  199.  */
  200.  
  201. char *strdupFAM(char * s);
  202. void *mallocFAM(unsigned size);
  203. void freeFAM(void * p);
  204.  
  205. char *strdupFAM(char * s)
  206. {
  207.     char * p = mallocFAM(strlen(s)+1);
  208.     if (p) strcpy(p, s);
  209.     return p;
  210.     }
  211.  
  212. void * mallocFAM(unsigned size)
  213. {
  214.     long * p = (long *) AllocMem(size+4, MEMF_CLEAR);
  215.     if (p) {
  216.     *p++ = (long) size;
  217.     }
  218.     return p;
  219.     }
  220.  
  221. void freeFAM(void * ptr)
  222. {
  223.     long * p = ptr;
  224.     long size = *--p;
  225.     FreeMem(p, size+4);
  226.     }
  227.  
  228. /*
  229.  *   This is our dispatch function.  We call our handler function.
  230.  *   If our handler replied, we return a 1 to indicate that.
  231.  *   If the parse and everything else was successful, we return a 0.
  232.  *   Otherwise, we return a failure of 10 to indicate that the arguments
  233.  *   were messed up.
  234.  */
  235. int disp(struct RexxMsg *msg, struct rexxCommandList *dat, char *p);
  236. int disp(struct RexxMsg *msg, struct rexxCommandList *dat, char *p)
  237. {
  238.     CMDparsed = 0 ;
  239.     CMDuserreplied = 0 ;
  240.     ((int (*)(struct RexxMsg *, char *))(dat->userdata))(msg, p) ;
  241.     if (CMDparsed == 0) {
  242.     if (!CMDuserreplied)
  243.         replyRexxCmd(msg, 0L, 0L, NULL) ;
  244.     CMDuserreplied = 1;
  245.     }
  246.     else {
  247.     replyRexxCmd(msg, ERRLEVEL, (long) CMDparsed, NULL) ;
  248.     CMDuserreplied = 1;
  249.     }
  250.     return CMDuserreplied;
  251.     }
  252.  
  253.  
  254. void errout(char * s, int exiting);
  255. void errout(char * s, int exiting)
  256. {
  257. #ifdef CBACK
  258.     if (_Backstdout == NULL) {
  259.     _Backstdout = Open("CON:0/0/400/100/RSD output", MODE_NEWFILE);
  260.     }
  261.     if (_Backstdout)
  262.     Write(_Backstdout, s, (long)strlen(s));
  263. #else
  264.     fprintf(stderr, "%s", s);
  265. #endif
  266.  
  267. #ifdef CBACK
  268.     if (exiting && _Backstdout)
  269.     Close(_Backstdout);
  270.     if (exiting) Delay(500);
  271. #endif
  272.     if (exiting) {
  273.     fclose(stdin);
  274.     fclose(stdout);
  275. /* #ifdef CBACK */
  276.     fclose(stderr);
  277. /* #endif */
  278.     }
  279.     }
  280.  
  281. #ifdef LMS
  282.  
  283. /*
  284.  * This handles the Low Memory message coming from the ASDG Low Memory Server.
  285.  *
  286.  */
  287.  
  288. int HandleLMS(struct Message * msg);
  289. int HandleLMS(struct Message * msg)
  290. {
  291.     if (msg != (struct Message *) &LMSSpace)            /* EH? */
  292.     return 0;
  293.     if (LMSSpace.lm_flag != LM_LOW_MEMORY_CONDITION)    /* EH?? */
  294.     return 0;
  295.     LMSSpace.lm_flag = LM_CONDITION_ACKNOWLEDGED;    /* ASDG foolishness */
  296.     FAMclear(NULL, "");
  297.     return 0;
  298.     }
  299.  
  300. #endif
  301.  
  302. /*
  303.  * Our main routine
  304.  *
  305.  */
  306.  
  307. void main(int argc, char * * argv);
  308. void main(int argc, char * * argv)
  309. {
  310.     long rexxbit;        /* this is the Signal bit to wait for Rexx on */
  311.     BPTR olddir, newdir;
  312.     void FreeScanList(struct List * list);
  313.  
  314.     if (argc == 0) {
  315.     errout("Can't run from WorkBench yet!\n", 1);
  316.     exit(20);    /* can't run from workbench yet */
  317.     }
  318.  
  319.     errout(VersionString, 0);
  320.     errout("\n", 0);
  321.  
  322.     if (argc < 4 || 6 < argc) {
  323.     errout("Usage: " PROGNAME " portname dirpath grepfunc [binextens [bufendline]]\n", 1);
  324.     exit(20);
  325.     }
  326.  
  327.     PortName = argv[1];
  328.     DirPath = argv[2];
  329.     GrepFuncName = argv[3];
  330.     if (5 <= argc)
  331.     BinExtens = argv[4];
  332.     if (6 <= argc)
  333.     BufEndLine = argv[5];
  334.  
  335.     GrepEntry = LoadSeg(GrepFuncName);
  336.     if (GrepEntry == 0) {
  337.     char buf[100];
  338.     sprintf(buf, "Could not LoadSeg GREP function \"%s\"!\n", GrepFuncName);
  339.     errout(buf, 1);
  340.     exit(20);
  341.     }
  342.  
  343.     newdir = Lock(DirPath, ACCESS_READ);
  344.     if (newdir) {
  345.     olddir = CurrentDir(newdir);
  346.     /* UnLock(olddir); */   /* CBack seems to munch this */
  347.     }
  348.     else {
  349.     char buf[100];
  350.     sprintf(buf, "Could not change to directory \"%s\"!\n", DirPath);
  351.     errout(buf, 1);
  352.     UnLoadSeg(GrepEntry);
  353.     exit(20);
  354.     }
  355.  
  356.  
  357.     rexxbit = upRexxPort(PortName, rcl, NULL, &disp) ;
  358.     if (rexxbit == 0) {
  359.     char buf[100];
  360.     sprintf(buf, "Could not open ARexx port \"%s\"!\n", PortName);
  361.     errout(buf, 1);
  362.     UnLoadSeg(GrepEntry);
  363.     exit(20);
  364.     }
  365.  
  366. #ifdef LMS
  367.  
  368.     /* Attempt to use ASDG low memory server; don't gripe if not there */
  369.     LowMemBase = OpenLibrary(LMSName, 0L);
  370.     if (LowMemBase != NULL) {
  371.     if (0 == RegLowMemReq(PortName , &LMSSpace)) {
  372.         HandleNonRexx = HandleLMS;
  373.         LMSSpace.lm_flag = LM_CONDITION_ACKNOWLEDGED;
  374.         LowMemIsOpen = 1;
  375.         }
  376.     else {
  377.         CloseLibrary(LowMemBase);
  378.         LowMemBase = NULL;
  379.         }
  380.     }
  381.  
  382. #endif
  383.  
  384.  
  385.     {    /* all went well */
  386.     char buf[100];
  387.     sprintf(buf, "ARexx port \"%s\" now available.\n", PortName);
  388.     errout(buf, 1);
  389.     }
  390.  
  391.     /* Here, initialize the ScanList */
  392.     NewList(&ScanList); NewList(&TempScanList);
  393.     ScanList.lh_Type = NT_FILELIST;
  394.     TempScanList.lh_Type = NT_FILELIST;
  395.  
  396.     do {
  397.     Wait(rexxbit);
  398.     dispRexxPort();
  399.     } while (! (OpenCount == 0 && ExpungeWanted));
  400.  
  401.     if (GrepEntry)
  402.     UnLoadSeg(GrepEntry);
  403.     GrepEntry = NULL;
  404.  
  405. #ifdef LMS
  406.  
  407.     if (LowMemIsOpen) {
  408.     LMSSpace.lm_flag = 0;    /* cancel any incomming messages */
  409.     DeRegLowMemReq(PortName);
  410.     }
  411.     if (LowMemBase) {
  412.     CloseLibrary(LowMemBase);
  413.     LowMemBase = NULL;
  414.     }
  415.  
  416. #endif
  417.  
  418.     CurrentDir(olddir);     /* CBack doesn't like changing dirs */
  419.     UnLock(newdir);
  420.  
  421.     FreeScanList(&ScanList);
  422.     FreeScanList(&TempScanList);
  423.  
  424.     dnRexxPort();
  425.     exit(0);
  426.  
  427.     }
  428.  
  429.  
  430. /*****************************************************************
  431.  * Parsing routines
  432.  *****************************************************************/
  433.  
  434.  
  435. /*
  436.  *   This function takes a pointer to a pointer to a string, grabs the
  437.  *   next number, returns it, and advances the pointer to the string to
  438.  *   point after the number.
  439.  */
  440. long getnm(char ** where);
  441. long getnm(char ** where)
  442. {
  443.     char *p = *where;
  444.     long val = 0;
  445.     long gotone = 0;
  446.  
  447.     while (*p <= ' ' && *p)
  448.     p++;
  449.     if (!*p)
  450.     CMDparsed = 17;     /* not enuf args */
  451.     while ('0' <= *p && *p <= '9') {
  452.     gotone = 1;
  453.     val = 10 * val + *p++ - '0';
  454.     }
  455.     if (gotone == 0 && CMDparsed == 0)
  456.     CMDparsed = 47;     /* invalid args; not a number */
  457.     *where = p;
  458.     return val;
  459.     }
  460. /*
  461.  *   This function trys to find `n' numeric arguments in the command
  462.  *   string, and stuffs them into the CMDargs array.
  463.  */
  464. void parseargs(char * p, long n);
  465. void parseargs(char * p, long n)
  466. {
  467.     int i ;
  468.  
  469.     while (*p > ' ' && *p)
  470.     p++ ;
  471.     for (i=0; i<n && CMDparsed == 0; i++)
  472.     CMDargs[i] = getnm(&p) ;
  473.     }
  474.  
  475.  
  476. /*******************************************************************
  477.  * List manipulation and directory scanning routines
  478.  *******************************************************************/
  479.  
  480. /*
  481.  * This function takes a ScanListNode and deallocates it.
  482.  * The node MUST be Remove()d before calling this.
  483.  */
  484. void FreeNode(struct ScanListNode * node);
  485. void FreeNode(struct ScanListNode * node)
  486. {
  487.     assert(node);
  488.     assert(node->node.ln_Type == NT_FILENAME);
  489.     if (node->node.ln_Name) freeFAM(node->node.ln_Name);
  490.     if (node->filenote) freeFAM(node->filenote);
  491.     if (node->contents) freeFAM(node->contents);
  492.     freeFAM(node);
  493.     }
  494.  
  495. /*
  496.  * This function allocates a new node, clearing out most of the fields.
  497.  */
  498. struct ScanListNode * AllocNode(void);
  499. struct ScanListNode * AllocNode(void)
  500. {
  501.     struct ScanListNode * node;
  502.     node = mallocFAM(sizeof(struct ScanListNode));
  503.     /* mallocFAM MUST clear the memory it gets */
  504.     if (node != NULL)
  505.     node->node.ln_Type = NT_FILENAME;
  506.     return node;
  507.     }
  508.  
  509.  
  510.  
  511.  
  512.  
  513. /*
  514.  * This function takes a file info block and a ScanListNode and
  515.  * copies the information from the fib to the node. Only items
  516.  * that have changed actually get reallocated. If NULL is passed in for
  517.  * node, then node will be allocated.  If NULL is returned, then
  518.  * some memory allocation failed and THE NODE HAS BEEN FREED!
  519.  * Hence, this node must NOT be in a list when FillInNode is called!
  520.  */
  521.  
  522. struct ScanListNode * FillInNode(struct FileInfoBlock * fib, struct ScanListNode * node);
  523. struct ScanListNode * FillInNode(struct FileInfoBlock * fib, struct ScanListNode * node)
  524. {
  525.     long newdate, newtime;
  526.     if (node == NULL) node = AllocNode();
  527.     if (node == NULL) return NULL;
  528.  
  529.     assert(node->node.ln_Type == NT_FILENAME);
  530.  
  531.     if (node->node.ln_Name == NULL ||
  532.         0 != strcmp(node->node.ln_Name, fib->fib_FileName)) {
  533.     if (node->node.ln_Name != NULL)
  534.         freeFAM(node->node.ln_Name);
  535.     node->node.ln_Name = strdupFAM(fib->fib_FileName);
  536.     if (node->node.ln_Name == NULL) {
  537.         FreeNode(node);
  538.         return NULL;
  539.         }
  540.     }
  541.  
  542.     node->protection = fib->fib_Protection;
  543.     node->isdir = 0 < fib->fib_DirEntryType;
  544.     newdate = fib->fib_Date.ds_Days;
  545.     newtime = (fib->fib_Date.ds_Minute * 60L) +
  546.           (fib->fib_Date.ds_Tick / 50L);
  547.     if (newtime != node->filetime || newdate != node->filedate) {
  548.     /* assume line count didn't change if date didn't change */
  549.     node->sizelines = -1L;
  550.     }
  551.     node->filedate = newdate;
  552.     node->filetime = newtime;
  553.     node->sizebytes = fib->fib_Size;
  554.     node->isNewFile = node->isDeleted = node->isGrepped = 0;
  555.  
  556.     if (node->filenote == NULL ||
  557.         0 != strcmp(node->filenote, fib->fib_Comment)) {
  558.     if (node->filenote != NULL) {
  559.         freeFAM(node->filenote);
  560.         node->filenote = NULL;
  561.         }
  562.     if (0 < strlen(fib->fib_Comment))
  563.         node->filenote = strdupFAM(fib->fib_Comment);
  564.     if (node->node.ln_Name == NULL) {
  565.         FreeNode(node);
  566.         return NULL;
  567.         }
  568.     }
  569.  
  570.     if (node->sizelines == -1L && BinExtens) {
  571.     register unsigned long i;
  572.     char * fext;
  573.     /* First, check to see if it has an extension */
  574.     fext = strchr(node->node.ln_Name, '.');
  575.     if (fext && *fext) {
  576.         /* we found an extension */
  577.             /* brutally inefficient... */
  578.         for (i = 0; i < strlen(BinExtens); i++) {
  579.         if (    0 == strncmp(fext, &BinExtens[i], strlen(fext)) &&
  580.             (BinExtens[i+strlen(fext)] == '.' ||
  581.              BinExtens[i+strlen(fext)] == '\0') ) {
  582.             node->sizelines = -2L;  /* It's a binary all right */
  583.             break;
  584.             }
  585.         }
  586.         }
  587.     }
  588.  
  589.     if (node->sizelines == -1L && BinExtens) {
  590.     char * buf;
  591.     register char * pnt;
  592.     register long len;
  593.     register unsigned long i;
  594.     long count;
  595.  
  596.     BPTR handle = Open(node->node.ln_Name, MODE_OLDFILE);
  597.     if (handle == NULL) return node;    /* could not count */
  598.     buf = mallocFAM(10 + node->sizebytes);
  599.     if (buf == NULL) {
  600.         Close(handle);
  601.         return node;
  602.         }
  603.     len = Read(handle, buf, node->sizebytes + 5);
  604.     if (len != node->sizebytes) {
  605.         freeFAM(buf);
  606.         Close(handle);
  607.         return node;
  608.         }
  609.  
  610.     pnt = buf; count = 0;
  611.     while (pnt = strchr(pnt, '\n')) {count += 1; pnt += 1;}
  612.     node->sizelines = count;
  613.  
  614.     if (node->contents) {
  615.         freeFAM(node->contents);
  616.         node->contents = NULL;
  617.         }
  618.  
  619.     if (BufEndLine) {
  620.         /* first, toss old contents */
  621.         if (node->contents) {
  622.         freeFAM(node->contents);
  623.         node->contents = NULL;
  624.         }
  625.         /* see if we can find BufEndLine in file */
  626.         pnt = buf; len = strlen(BufEndLine);
  627.         while (pnt && 0 != strncmp(BufEndLine, pnt, len)) {
  628.         pnt = strchr(pnt, '\n');
  629.         if (pnt) pnt++;
  630.         }
  631.         if (pnt) {
  632.         /* we found BufEndLine -- move to contents */
  633.         i = pnt - buf;
  634.         node->contents = mallocFAM(i + 2);
  635.         if (node->contents) {
  636.             strncpy(node->contents, buf, i);
  637.             node->contents[i] = '\0';
  638.             }
  639.         }
  640.         }
  641.  
  642.     freeFAM(buf); Close(handle);
  643.     }
  644.  
  645.     return node;
  646.  
  647.     }
  648.  
  649. /*
  650.  * This function empties the entire list given to it.
  651.  */
  652. void FreeScanList(struct List * list);
  653. void FreeScanList(struct List * list)
  654. {
  655.     struct ScanListNode * node;
  656.     while (!(IsEmpty(*list))) {
  657.     node = (struct ScanListNode *) RemHead(list);
  658.     FreeNode(node);
  659.     }
  660.     }
  661.  
  662.  
  663. /* This is a secret communication path for optimization of RESCAN */
  664. static struct ScanListNode * InsAlphaPrev;
  665.  
  666. /*
  667.  * This function takes a ScanList and a string and finds the node
  668.  * in the list that the string would follow if it were the name
  669.  * of a node to be inserted alphabetically. Assumes the list is already
  670.  * alphabetical.
  671.  */
  672. struct ScanListNode * FindPrev(struct List * list, struct ScanListNode * node);
  673. struct ScanListNode * FindPrev(struct List * list, struct ScanListNode * node)
  674. {
  675.     struct ScanListNode * index;
  676.     char * name = node->node.ln_Name;
  677.  
  678.     if (IsEmpty(*list)) {
  679.     return (struct ScanListNode *) list;
  680.     }
  681.  
  682.     if (InsAlphaPrev && StrLT(InsAlphaPrev->node.ln_Name, name))
  683.     index = InsAlphaPrev;
  684.     else
  685.     index = FirstNode(list);
  686.  
  687.     for ( ; NotLastNode(index); index = NextNode(index)) {
  688.     if (StrLT(name, index->node.ln_Name)) {
  689.         return (struct ScanListNode *) (index->node.ln_Pred);
  690.         }
  691.     }
  692.  
  693.     return (struct ScanListNode *) (&(list->lh_Tail));
  694.     }
  695.  
  696.  
  697. /*
  698.  * This function takes a node and a list and inserts the node in
  699.  * the correct alphabetical place in the list. The node should not be part
  700.  * of another list at the time.
  701.  */
  702. void InsAlpha(struct List * list, struct ScanListNode * node);
  703. void InsAlpha(struct List * list, struct ScanListNode * node)
  704. {
  705.     struct ScanListNode * prev;
  706.     if (InsAlphaPrev == node) InsAlphaPrev = NULL;
  707.     prev = FindPrev(list, node);
  708.     Insert(list, (struct Node *) node, (struct Node *) prev);
  709.     }
  710.  
  711.  
  712.  
  713.  
  714. /*******************************************************************
  715.  * Here we have the actual command-handling code.
  716.  *******************************************************************/
  717.  
  718.  
  719. /*
  720.  *   This handler returns the version of the program.
  721.  */
  722. void FAMversion(struct RexxMsg *msg, char *p)
  723. {
  724.     CMDuserreplied = 1 ;
  725.     replyRexxCmd(msg, 0L, 0L, VersionString);
  726.     }
  727.  
  728.  
  729. /*
  730.  * This handler opens the device or file.
  731.  */
  732. void FAMopen(struct RexxMsg *msg, char *p)
  733. {
  734.     char buf[20];
  735.  
  736.     if (IsEmpty(ScanList)) {
  737.     FAMrescan(msg, "");
  738.     }
  739.     if (IsEmpty(ScanList)) {
  740.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  741.     CMDuserreplied = 1;
  742.     }
  743.     else {
  744.     OpenCount += 1;
  745.     sprintf(buf, "%d", OpenCount);
  746.     replyRexxCmd(msg, 0L, 0L, buf);
  747.     CMDuserreplied = 1;
  748.     }
  749.     }
  750.  
  751. /*
  752.  * This handler closes the device or file.
  753.  */
  754. void FAMclose(struct RexxMsg *msg, char *p)
  755. {
  756.     char buf[20];
  757.     while (*p <= ' ' && *p) p++;
  758.     if (OpenCount == 0) {
  759.     return;
  760.     }
  761.     if (0 == strcmp(p, "NOW") && 1 < OpenCount) {
  762.     OpenCount = 1;
  763.     }
  764.     OpenCount -= 1;
  765.     if (OpenCount == 0 && ClearWanted) {
  766.     FreeScanList(&ScanList);
  767.     FreeScanList(&TempScanList);
  768.     ClearWanted = 0;
  769.     }
  770.     sprintf(buf, "%d", OpenCount);
  771.     if (msg) {
  772.     replyRexxCmd(msg, 0L, 0L, buf);
  773.     CMDuserreplied = 1;
  774.     }
  775.     }
  776.  
  777. /*
  778.  * This handler sets the clear flag and possibly clears the ScanList.
  779.  * It does not reply if msg is NULL, as we might call it from the
  780.  * ASDG low memory handler.
  781.  */
  782. void FAMclear(struct RexxMsg *msg, char *p)
  783. {
  784.     char buf[20];
  785.     if (IsEmpty(ScanList)) {
  786.     return;
  787.     }
  788.     if (OpenCount == 0) {
  789.     FreeScanList(&ScanList);
  790.     FreeScanList(&TempScanList);
  791.     }
  792.     else {
  793.     ClearWanted = 1;
  794.     }
  795.     if (msg) {
  796.     sprintf(buf, "%d", OpenCount);
  797.     replyRexxCmd(msg, 0L, 0L, buf);
  798.     CMDuserreplied = 1;
  799.     }
  800.     }
  801.  
  802. /*
  803.  * This handler expunges the program.
  804.  */
  805. void FAMexpunge(struct RexxMsg *msg, char *p)
  806. {
  807.     ExpungeWanted = 1;
  808.     ClearWanted = 1;
  809.     while (*p <= ' ' && *p) p++;
  810.     if (0 == strcmp(p, "NOW")) {
  811.     if (OpenCount) FAMclose(msg, p);
  812.     }
  813.     }
  814.  
  815. /*
  816.  * This handler returns the most recent IO error number.
  817.  */
  818. void FAMgetioerr(struct RexxMsg *msg, char *p)
  819. {
  820.     long ans;
  821.     char buf[20];
  822.  
  823.     ans = IoErr();
  824.     sprintf(buf, "%ld", ans);
  825.  
  826.     replyRexxCmd(msg, 0L, 0L, buf);  /* generally errored */
  827.     CMDuserreplied = 1;
  828.  
  829.     }
  830.  
  831. /*
  832.  *   This handler returns the directory path.
  833.  */
  834. void FAMgetdirname(struct RexxMsg *msg, char *p)
  835. {
  836.     CMDuserreplied = 1 ;
  837.     replyRexxCmd(msg, 0L, 0L, DirPath);
  838.     }
  839.  
  840.  
  841. /*
  842.  *   This handler returns the binary extensions.
  843.  */
  844. void FAMgetbinextens(struct RexxMsg *msg, char *p)
  845. {
  846.     CMDuserreplied = 1 ;
  847.     if (BinExtens)
  848.     replyRexxCmd(msg, 0L, 0L, BinExtens);
  849.     else
  850.     replyRexxCmd(msg, 1L, 0L, NULL);
  851.     }
  852.  
  853.  
  854. /*
  855.  *   This handler returns the buffer end line.
  856.  */
  857. void FAMgetbufendline(struct RexxMsg *msg, char *p)
  858. {
  859.     CMDuserreplied = 1 ;
  860.     if (BufEndLine)
  861.     replyRexxCmd(msg, 0L, 0L, BufEndLine);
  862.     else
  863.     replyRexxCmd(msg, 1L, 0L, NULL);
  864.     }
  865.  
  866.  
  867. /*
  868.  * This handler returns the current count of names.
  869.  */
  870. void FAMgetnamecount(struct RexxMsg *msg, char *p)
  871. {
  872.     struct ScanListNode * node;
  873.     long count;
  874.     char buf[20];
  875.  
  876.     if (IsEmpty(ScanList)) {
  877.     count = 0;
  878.     }
  879.     else {
  880.     count = 1;
  881.     for (node = FirstNode(&ScanList); NotLastNode(node);
  882.             node = NextNode(node)) {
  883.         count += 1;
  884.         }
  885.     }
  886.     sprintf(buf, "%ld", count);
  887.     replyRexxCmd(msg, 0L, 0L, buf);
  888.     CMDuserreplied = 1;
  889.     }
  890.  
  891. /*
  892.  * This handler returns the current list of names.
  893.  */
  894. void FAMgetnames(struct RexxMsg *msg, char *p)
  895. {
  896.     char * __stdargs ListNames(struct List *, char);    /* in RexxGlue */
  897.     void __stdargs DeleteArgstring(char * arg);         /* in RexxGlue */
  898.     char * result;
  899.     char pad = ' ';
  900.  
  901.     while (*p <= ' ' && *p) p++;
  902.     if (' ' < *p && *p <= 0x7F) pad = *p;
  903.  
  904.     if (0 == openRexxLib()) {
  905.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  906.     CMDuserreplied = 1;
  907.     }
  908.  
  909.     result = ListNames(&ScanList, pad);
  910.     if (result) {
  911.     replyRexxCmd(msg, 0L, 0L, result);
  912.     CMDuserreplied = 1;
  913.     openRexxLib();  /* because replyRexxCmd closes it! */
  914.     DeleteArgstring(result);
  915.     }
  916.     else {
  917.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  918.     CMDuserreplied = 1;
  919.     }
  920.  
  921.     closeRexxLib();
  922.  
  923.     }
  924.  
  925. /*
  926.  * This handler returns the information for one file.
  927.  */
  928. void FAMgetinfo(struct RexxMsg *msg, char *p)
  929. {
  930.     static char protchars[33] = "DEWRAPSH";  /* allow room for patching */
  931.     char buf[350];
  932.     unsigned short len, i;
  933.     char c;
  934.     struct ScanListNode * node;
  935.     char * argbuf;
  936.  
  937.     while (*p <= ' ' && *p) p++;
  938.     if (!*p) {
  939.     CMDparsed = 17;
  940.     return;
  941.     }
  942.  
  943.     node = (struct ScanListNode *) FindName(&ScanList, p);
  944.     if (node == NULL) {
  945.     CMDparsed = 18;
  946.     return;
  947.     }
  948.  
  949.     strcpy(buf, node->isdir ? "DIR -" : "FILE -");
  950.     /* '-' starts protection bits */
  951.  
  952.     for (len = strlen(buf), i = 0; i < strlen(protchars); i++) {
  953.     if (0 == (node->protection & (1 << i))) {
  954.         buf[len++] = protchars[i];
  955.         }
  956.     }
  957.  
  958.     c = '-';
  959.     if (node->isLocked)  c = 'L';
  960.     if (node->isNewFile) c = 'N';
  961.  
  962.     sprintf(&buf[len], " %ld %ld %ld %ld %c ",
  963.         node->sizebytes, node->sizelines,
  964.         node->filedate, node->filetime, c);
  965.  
  966.     strcat(buf, node->node.ln_Name);
  967.     strcat(buf, " ");
  968.     if (node->filenote)
  969.     strcat(buf, node->filenote);
  970.  
  971.     if (sizeof(buf) <= strlen(buf)) exit(250);
  972.  
  973.     if (node->contents) {
  974.     argbuf = mallocFAM(strlen(node->contents)+strlen(buf)+5);
  975.     if (argbuf) {
  976.         strcpy(argbuf, buf);
  977.         strcat(argbuf, "\n");
  978.         strcat(argbuf, node->contents);
  979.         replyRexxCmd(msg, 0L, 0L, argbuf);
  980.         CMDuserreplied = 1;
  981.         freeFAM(argbuf);
  982.         }
  983.     else {
  984.         replyRexxCmd(msg, 0L, 0L, buf);
  985.         CMDuserreplied = 1;
  986.         }
  987.     }
  988.     else {
  989.     replyRexxCmd(msg, 0L, 0L, buf);
  990.     CMDuserreplied = 1;
  991.     }
  992.     }
  993.  
  994. /*
  995.  * This handler reconstructs the information for one file.
  996.  */
  997. void FAMrescan1(struct RexxMsg *msg, char *p)
  998. {
  999.     struct ScanListNode * node;
  1000.     struct FileInfoBlock * fib = malloc(sizeof(struct FileInfoBlock));
  1001.     BPTR lock;
  1002.     char buf[20];
  1003.  
  1004.     while (*p <= ' ' && *p) p++;
  1005.     if (*p == 0 || fib == NULL) {
  1006.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  1007.     CMDuserreplied = 1;
  1008.     return;
  1009.     }
  1010.  
  1011.     lock = Lock(p, SHARED_LOCK);
  1012.     if (!lock) {
  1013.     free(fib);
  1014.     node = (struct ScanListNode *) FindName(&ScanList, p);
  1015.     if (node) {
  1016.         Remove((struct Node *) node);
  1017.         FreeNode(node);
  1018.         }
  1019.     sprintf(buf, "%ld", IoErr());
  1020.     replyRexxCmd(msg, 0L, 0L, buf);
  1021.     CMDuserreplied = 1;
  1022.     return;
  1023.     }
  1024.  
  1025.     if (!Examine(lock, fib)) {
  1026.     sprintf(buf, "%ld", IoErr());
  1027.     replyRexxCmd(msg, 0L, 0L, buf);
  1028.     CMDuserreplied = 1;
  1029.     free(fib);
  1030.     UnLock(lock);
  1031.     return;
  1032.     }
  1033.  
  1034.     UnLock(lock);
  1035.  
  1036.     node = (struct ScanListNode *) FindName(&ScanList, p);
  1037.     if (node) {
  1038.     Remove((struct Node *) node);
  1039.     }
  1040.  
  1041.     node = FillInNode(fib, node);
  1042.     if (node == NULL) {
  1043.     free(fib);
  1044.     sprintf(buf, "%ld", IoErr());
  1045.     replyRexxCmd(msg, 0L, 0L, buf);
  1046.     CMDuserreplied = 1;
  1047.     return;
  1048.     }
  1049.  
  1050.     InsAlpha(&ScanList, node);
  1051.  
  1052.     sprintf(buf, "%ld", IoErr());
  1053.     replyRexxCmd(msg, 0L, 0L, buf);
  1054.     CMDuserreplied = 1;
  1055.     }
  1056.  
  1057. /*
  1058.  * This handler reconstructs the information for all files.
  1059.  */
  1060. void FAMrescan(struct RexxMsg *msg, char *p)
  1061. {
  1062.     /* right now, the argument is ignored */
  1063.  
  1064.     struct ScanListNode * node, * next;
  1065.     struct FileInfoBlock * fib = malloc(sizeof(struct FileInfoBlock));
  1066.     BPTR lock;
  1067.  
  1068. #ifdef DEBUG
  1069.     long c1 = 0, c2 = 0, c3 = 0;    /* performance measurement */
  1070. #endif
  1071.  
  1072.     while (*p <= ' ' && *p) p++;
  1073.     if (fib == NULL) {
  1074.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  1075.     CMDuserreplied = 1;
  1076.     return;
  1077.     }
  1078.  
  1079.     lock = Lock("", SHARED_LOCK); /* always works on current dir for now */
  1080.     if (!lock) {
  1081.     free(fib);
  1082.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  1083.     CMDuserreplied = 1;
  1084.     return;
  1085.     }
  1086.  
  1087.     if (!Examine(lock, fib)) {
  1088.     free(fib);
  1089.     UnLock(lock);
  1090.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  1091.     CMDuserreplied = 1;
  1092.     return;
  1093.     }
  1094.  
  1095.     /* delete all files except NEWFILE files */
  1096.     for (node = FirstNode(&ScanList); NotLastNode(node); node = NextNode(node)) {
  1097.     if (!node->isNewFile)
  1098.         node->isDeleted = 1;  /* set it to deleted */
  1099.     }
  1100.  
  1101.     while (ExNext(lock, fib)) {
  1102.     node = (struct ScanListNode *) FindName(&ScanList, fib->fib_FileName);
  1103.     if (node != NULL && !node->isNewFile)
  1104.         Remove((struct Node *) node);
  1105.     if (node == NULL || !node->isNewFile)
  1106.         node = FillInNode(fib, node);
  1107.     if (node == NULL) {
  1108.         free(fib);
  1109.         UnLock(lock);
  1110.         replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  1111.         CMDuserreplied = 1;
  1112.         return;
  1113.         }
  1114.     else {
  1115.         /* see if we know where to put it already */
  1116.         if (    !IsEmpty(ScanList) &&
  1117.             StrLT(ScanList.lh_TailPred->ln_Name,
  1118.               node->node.ln_Name) ) {
  1119.         AddTail(&ScanList, (struct Node *) node);
  1120.         InsAlphaPrev = node;
  1121.         #ifdef DEBUG
  1122.         c1 += 1;
  1123.         #endif
  1124.         }
  1125.         else if(node->node.ln_Pred &&               /* has pred *
  1126.             node->node.ln_Succ &&        /* has succ */
  1127.             node->node.ln_Pred->ln_Pred &&    /* not at start */
  1128.             node->node.ln_Succ->ln_Succ &&    /* not at end */
  1129.             StrLT(node->node.ln_Pred->ln_Name,  /* after pred */
  1130.               node->node.ln_Name) &&
  1131.             StrLT(node->node.ln_Name,           /* before succ */
  1132.               node->node.ln_Succ->ln_Name) ) {
  1133.         InsertQ((struct Node *) node);
  1134.         InsAlphaPrev = node;
  1135.         #ifdef DEBUG
  1136.         c2 += 1;
  1137.         #endif
  1138.         }
  1139.         else {
  1140.         InsAlpha(&ScanList, node);
  1141.         InsAlphaPrev = node;
  1142.         #ifdef DEBUG
  1143.         c3 += 1;
  1144.         #endif
  1145.         }
  1146.         }
  1147.     }
  1148.  
  1149.     UnLock(lock);       /* make FileSystem designers happy */
  1150.     InsAlphaPrev = NULL;
  1151.  
  1152.     for (node = FirstNode(&ScanList); NotLastNode(node); ) {
  1153.     assert(node && node->node.ln_Type == NT_FILENAME);
  1154.     if (node->isDeleted) {
  1155.         /* We never saw this node during the rescan! */
  1156.         next = NextNode(node);
  1157.         if (next != NULL) {
  1158.         Remove((struct Node *) node);
  1159.         FreeNode(node);
  1160.         }
  1161.         node = next;
  1162.         }
  1163.     else {
  1164.         node = NextNode(node);
  1165.         }
  1166.     }
  1167.     }
  1168.  
  1169. /*
  1170.  * This handler matches general user information.
  1171.  */
  1172. void FAMgrep(struct RexxMsg *msg, char *p)
  1173. {
  1174.     struct ScanListNode * node;
  1175.     long count = 0;
  1176.     char * buf;
  1177.     char pad[2] = " ";
  1178.  
  1179.     if (' ' < *p && *p <= 0x7F)
  1180.     pad[0] = *p++;
  1181.  
  1182.     while (*p <= ' ' && *p) p++;
  1183.  
  1184.     if (GrepEntry == NULL) {
  1185.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  1186.     CMDuserreplied = 1;
  1187.     return;
  1188.     }
  1189.  
  1190.     for (node = FirstNode(&ScanList); NotLastNode(node); node = NextNode(node)) {
  1191.     node->isGrepped = 0;
  1192.     if ((*(GrepFunc *)(GrepEntry<<2L))(node, p)) {
  1193.         node->isGrepped = 1;
  1194.         count += strlen(node->node.ln_Name) + 1;
  1195.         }
  1196.     }
  1197.  
  1198.     if (count == 0) {
  1199.     replyRexxCmd(msg, 0L, 0L, "");
  1200.     CMDuserreplied = 1;
  1201.     return;
  1202.     }
  1203.  
  1204.     buf = mallocFAM(count + 4);
  1205.     if (buf == NULL) {
  1206.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  1207.     CMDuserreplied = 1;
  1208.     return;
  1209.     }
  1210.  
  1211.     buf[0] = '\0';
  1212.     for (node = FirstNode(&ScanList); NotLastNode(node); node = NextNode(node)) {
  1213.     if (node->isGrepped) {
  1214.         strcat(buf, pad);
  1215.         strcat(buf, node->node.ln_Name);
  1216.         node->isGrepped = 0;
  1217.         }
  1218.     }
  1219.  
  1220.     replyRexxCmd(msg, 0L, 0L, &buf[1]); /* skip initial pad */
  1221.     CMDuserreplied = 1;
  1222.     freeFAM(buf);
  1223.  
  1224.     }
  1225.  
  1226. /*
  1227.  * This handler generates a new pseudo-file.
  1228.  */
  1229. void FAMnewfile(struct RexxMsg *msg, char *p)
  1230. {
  1231.     struct ScanListNode * node;
  1232.     long num, len;
  1233.     short full;
  1234.     short base;
  1235.     char * basepnt;
  1236.     char * name, * cp;
  1237.     char buf[80];
  1238.     extern long __stdargs CVa2i(char *, long *);          /* in RexxGlue.a */
  1239.     extern void __stdargs CVi2az(char *, long, long);     /* in RexxGlue.a */
  1240.  
  1241.     if (p[0] == '3' && p[1] == '6') {
  1242.     base = 36;
  1243.     p += 2;
  1244.     basepnt = "0123456789ABCDEFGHIJLMNOPQRSTUVWXYZ";
  1245.     }
  1246.     else {
  1247.     /* assume base 10 */
  1248.     if (p[0] == '1' && p[1] == '0') p += 2;
  1249.     basepnt = "0123456789";
  1250.     base = 10;
  1251.     }
  1252.  
  1253.     while (*p <= ' ' && *p) p++;
  1254.  
  1255.     if (IsEmpty(ScanList) || !*p) {
  1256.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  1257.     CMDuserreplied = 1;
  1258.     return;
  1259.     }
  1260.  
  1261.     for (node = FirstNode(&ScanList); NotLastNode(node); node = NextNode(node)) {
  1262.     if (0 == strncmp(p, node->node.ln_Name, strlen(p))) {
  1263.         break;
  1264.         }
  1265.     }
  1266.     if (node == NULL) {
  1267.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  1268.     CMDuserreplied = 1;
  1269.     return;
  1270.     }
  1271.  
  1272.     for ( ; NotLastNode(node); node = NextNode(node)) {
  1273.     if (0 != strncmp(p, node->node.ln_Name, strlen(p))) {
  1274.         break;
  1275.         }
  1276.     }
  1277.     node = (struct ScanListNode *) node->node.ln_Pred;
  1278.  
  1279.     /* at this point, 'node' points to the node our's should follow */
  1280.  
  1281.     name = node->node.ln_Name;
  1282.     if (0 != strncmp(p, name, strlen(p))) {
  1283.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  1284.     CMDuserreplied = 1;
  1285.     return;
  1286.     }
  1287.  
  1288.     node = AllocNode();
  1289.  
  1290.     if (0 == openRexxLib() || node == NULL) {
  1291.     if (node) FreeNode(node);
  1292.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  1293.     CMDuserreplied = 1;
  1294.     return;
  1295.     }
  1296.  
  1297.     for (       cp = name + strlen(p), len = 0, num = 0, full = 1;
  1298.         isdigit(*cp);
  1299.         cp++, len++) {
  1300.     full = full && *cp == '9';
  1301.     num = num * 10 + *cp - '0';
  1302.     }
  1303.  
  1304.     if (full) {
  1305.     FreeNode(node);
  1306.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  1307.     CMDuserreplied = 1;
  1308.     return;
  1309.     }
  1310.  
  1311.     strcpy(buf, p);
  1312.     CVi2az(&buf[strlen(p)], num + 1, len);
  1313.  
  1314.     node->node.ln_Name = strdupFAM(buf);
  1315.     if (node->node.ln_Name == NULL) {
  1316.     FreeNode(node);
  1317.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  1318.     CMDuserreplied = 1;
  1319.     return;
  1320.     }
  1321.  
  1322.     node->isNewFile = 1;  /* it's a NEWFILE file */
  1323.  
  1324.     InsAlpha(&ScanList, node);
  1325.  
  1326.     replyRexxCmd(msg, 0L, 0L, node->node.ln_Name);
  1327.     CMDuserreplied = 1;
  1328.  
  1329.     }
  1330.  
  1331. /*
  1332.  * This handler locks or unlocks a file.
  1333.  */
  1334. void FAMlockfile(struct RexxMsg *msg, char *p)
  1335. {
  1336.     struct ScanListNode * node;
  1337.     char old = 0, new = 0;
  1338.     int good = 1;
  1339.  
  1340.     while (*p <= ' ' && *p) p++;
  1341.     if (*p) old = *p++;
  1342.  
  1343.     while (*p <= ' ' && *p) p++;
  1344.     if (*p) new = *p++;
  1345.  
  1346.     while (*p <= ' ' && *p) p++;
  1347.  
  1348.     if (old != '-' && old != 'L') old = 0;
  1349.     if (new != '-' && new != 'L') new = 0;
  1350.  
  1351.     if (IsEmpty(ScanList) || !*p || old == 0 || new == 0) {
  1352.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  1353.     CMDuserreplied = 1;
  1354.     return;
  1355.     }
  1356.  
  1357.     for (node = FirstNode(&ScanList); NotLastNode(node); node = NextNode(node)) {
  1358.     if (0 == strcmp(p, node->node.ln_Name)) {
  1359.         break;
  1360.         }
  1361.     }
  1362.     if (node == NULL) {
  1363.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  1364.     CMDuserreplied = 1;
  1365.     return;
  1366.     }
  1367.  
  1368.     good = good && (node->isNewFile == 0);  /* can't lock NewFiles */
  1369.     good = good && ((old == 'L') == node->isLocked);
  1370.     if (good) {
  1371.     node->isLocked = (new == 'L');
  1372.     }
  1373.     else {
  1374.     replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
  1375.     CMDuserreplied = 1;
  1376.     return;
  1377.     }
  1378.  
  1379.     }
  1380.  
  1381.  
  1382.